package raft

import "sync/atomic"

type AppendEntriesArgs struct {
	Term         int
	LeaderId     int
	PrevLogIndex int
	PrevLogTerm  int
	Entries      []Entry
	LeaderCommit int
}

type AppendEntriesReply struct {
	Term         int
	Success      bool
	LastLogIndex int
	LastLogTerm  int
}

func (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply) {
	rf.mu.Lock()
	defer rf.mu.Unlock()

	reply.Term = rf.currentTerm

	if args.Term < rf.currentTerm {
		reply.Success = false
		DPrintf("[%d] follower term %v >= %d leader term %d", rf.me, rf.currentTerm, args.LeaderId, args.Term)
		return
	}

	rf.currentTerm = args.Term
	atomic.StoreInt32(&rf.state, Follower)

	if args.PrevLogIndex >= len(rf.log) || args.PrevLogTerm != rf.log[args.PrevLogIndex].Term {
		if args.PrevLogIndex >= len(rf.log) {
			rf.log = rf.log[:len(rf.log)]
		} else {
			rf.log = rf.log[:args.PrevLogIndex]
		}
		reply.LastLogIndex = rf.getLastLogIndex()
		reply.LastLogTerm = rf.getLastLogTerm()
		reply.Success = false
		rf.persist()
		rf.tickerCh <- struct{}{}
		return
	}

	if len(args.Entries) > 0 {
		rf.log = append(rf.log[:args.PrevLogIndex+1], args.Entries...)
		DPrintf("[%d] follower appended %d entries %+v to %+v", rf.me, args.PrevLogIndex+1, args.Entries[0], args.Entries[len(args.Entries)-1])
	}
	rf.persist()

	rf.commitIndex = Min(args.LeaderCommit, rf.getLastLogIndex())

	if rf.commitIndex > rf.lastApplied {
		/*
			applied := make([]Entry, rf.commitIndex-rf.lastApplied)
			copy(applied, rf.log[rf.lastApplied+1:rf.commitIndex+1])
			go func(bp int, entries []Entry) {
				for i, entry := range entries {
					rf.applyCh <- ApplyMsg{
						CommandValid: true,
						Command:      entry.Command,
						CommandIndex: bp + i,
					}
				}
			}(rf.lastApplied+1, applied)
		*/
		for i := rf.lastApplied + 1; i <= rf.commitIndex; i++ {
			rf.applyCh <- ApplyMsg{
				CommandValid: true,
				Command:      rf.log[i].Command,
				CommandIndex: i,
			}
		}
		rf.lastApplied = rf.commitIndex
		DPrintf("[%d] follower applied %d entry %+v", rf.me, rf.lastApplied, rf.log[rf.lastApplied])
	}

	reply.LastLogIndex = rf.getLastLogIndex()
	reply.LastLogTerm = rf.getLastLogTerm()
	reply.Success = true

	rf.tickerCh <- struct{}{}
}
